﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Reflection;
using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;
using System.Collections;

namespace DirectX_CS
{
	public partial class ObjectsEditor : Form
	{
		Device mDevice = null;
		PrimitiveManager mRenderer = null;
        MainProgram mMainProgram = null;
		public bool initializing = true;

        protected List<ObjectListItem> mList = null;

		private static List<object> mStaticObjectsList = null;
        private static object mSelectedObject = null;
		private static ObjectsEditor thisOnlyForm = null;

		private static List<object> mUserObjectList = new List<object>();

		public static List<object> UserObjectsList
		{
			get { return ObjectsEditor.mUserObjectList.ToList<object>(); }
		}
		public static object[] GetCurrentViewableList()
		{
			if (thisOnlyForm == null)
				return null;

			if (thisOnlyForm.lbPrimitives.DataSource is IList)
			{
				IList list = thisOnlyForm.lbPrimitives.DataSource as IList;
				IList<object> valueList = (from ObjectListItem li in list select li.ObjectAssociated).ToList<object>();
				return valueList.ToArray<object>();
			}
			else
				return null;
		}
		public void AddStaticObjectOrList(object userAddedObject)
		{
			if (userAddedObject != null)
			{
				if (userAddedObject is IList)
				{
					Debug.WriteLine("Ilist");
					IList list = (IList)userAddedObject;
					foreach (object o in list)
					{
						AddStaticObjectOnce(o);
					}
				}
				else
					AddStaticObjectOnce(userAddedObject);
			}

		}
		public void AddUserObject(object userAddedObject)
        {
			if (userAddedObject != null)
			{
				if (mUserObjectList.Find(delegate (object o ) {return o ==userAddedObject; }) == null) 
					mUserObjectList.Add(userAddedObject);
			}
		}

		private void AddStaticObjectOnce(object obj)
		{
			if (!mStaticObjectsList.Contains(obj))
			{
				mStaticObjectsList.Add(obj);

				if (!initializing)
					ReloadList();
			}
		}
		
		public static ObjectsEditor OnlyForm
		{
			get { return thisOnlyForm; }
		}

		public static void ReloadList()
		{
			if (OnlyForm != null)
				OnlyForm.ReloadAllObjects();
		}
		public static object Selected
		{
			get { return mSelectedObject; }
			set { mSelectedObject = value;
				if (OnlyForm != null)
				{
					OnlyForm.FindSelectedPrimitive();
					if (SelectMesh.OnlyObject != null)
					{
						SelectMesh.OnlyObject.Recreate();
					}
				}
			}
		}

		public ObjectsEditor(Device theDevice, PrimitiveManager renderer, MainProgram mainProgramWithWindows)
		{
			initializing = true;
			InitializeComponent();
			mDevice = theDevice;
			mRenderer = renderer;
            mMainProgram = mainProgramWithWindows;
            mStaticObjectsList = new List<object>();
			thisOnlyForm = this;
			SetPGBrowsableAttributes(tsmiCustomAttributesOnly.Checked);
			ReloadAllObjects();

		}
        public static int CompareObjectName (object lhs, object rhs) 
        { 
            return lhs.GetType().Name.CompareTo(rhs.GetType().Name) ; 
        }

		public void ReloadAllObjects()
		{				
			initializing = true;
			List<object> mOriginalDataSource =  new List<object>();

			AddStaticObjectOrList(MainProgram.OnlyProgram);
			//AddStaticObjectOrList(SelectMesh.OnlyObject);	 //it should be registered so no need to call to add it to the list since its in the registered primitives already.
			AddStaticObjectOrList(Camera.CameraForms);
			AddStaticObjectOrList(RenderStateViewer.OnlyForm);
			AddStaticObjectOrList(Mouse.MouseForms);
			AddStaticObjectOrList(OutputForm.PrimaryOutputForm);
			AddStaticObjectOrList((Form)Lighting.OnlyForm);

			if (tsmiDisplayStaticObjects.Checked == true)
				mOriginalDataSource.AddRange(mStaticObjectsList);

			List<object> primList = mRenderer.RegisteredPrimitives;

			if (tsmiDisplayPrimitives.Checked)
				foreach (object prim in primList)
					mOriginalDataSource.Add(prim);

			if (mOriginalDataSource != null && mOriginalDataSource.Count != 0)
            {
                //sort by name then rename numerical repetitions of name by number
                mOriginalDataSource.Sort(CompareObjectName);
				mList = new List<ObjectListItem>();
            
				int seriesNumber = 0;
				int objIndex = 0;

				ObjectListItem primListItem = null;
				ObjectListItem firstItemInSeries = null;
				primListItem = new ObjectListItem(mOriginalDataSource[objIndex]);
				primListItem.ShortName = primListItem.ObjectAssociated.GetType().Name;

				for (objIndex = 0; objIndex < mOriginalDataSource.Count; /*counting done already*/)
				{

					firstItemInSeries = primListItem;
					seriesNumber = 0;
					do
					{
						primListItem.SeriesNumber = seriesNumber;
						primListItem.ShortName = primListItem.ObjectAssociated.GetType().Name;
						mList.Add(primListItem);

						if (primListItem.UniqueName == firstItemInSeries.UniqueName) 
						{

							seriesNumber++;
						}
						objIndex++;
						if (objIndex < mOriginalDataSource.Count)
							primListItem = new ObjectListItem(mOriginalDataSource[objIndex], seriesNumber);
					} while (objIndex < mOriginalDataSource.Count && primListItem.UniqueName == firstItemInSeries.UniqueName);
				}


                lbPrimitives.DataSource = mList; //mFilteredList
				lbPrimitives.DisplayMember = "UniqueName";
				lbPrimitives.ValueMember = "ObjectAssociated";
				lbPrimitives.Update();

				FindSelectedPrimitive();

				initializing = false;
			}
		}
        protected List<ObjectListItem> ListItems
        {
            get {
               return mList;
            }
        }
		private void FindSelectedPrimitive()
		{
			//find the selected primitiveWithVertices even though the list changed.
			if (mSelectedObject != null)
			{
				//select the same prim from the new list
				bool found = false;
				int idx = -1;
				if (ListItems == null)
					found = false;
				else
				{
					for (idx = 0; idx < ListItems.Count; idx++)
					{
						ObjectListItem listItem = ListItems[idx];
						if (listItem.ObjectAssociated == mSelectedObject)
						{
							found = true;
							break;
						}
					}
				}

                if (found)
                {
					lbPrimitives.SelectedIndex = -1;
                    lbPrimitives.SelectedIndex = idx;
                }
                else
                {
                    //leave this one selected
                }
			}
			else
			{
				//select a new prim
                //if (ListItems != null && ListItems.Count > 0)
                //{
                //    lbPrimitives.SelectedIndex = -1;
                //    mSelectedPrimitive = lbPrimitives.SelectedValue;
                //}
                lbPrimitives.SelectedIndex = -1;
			}//end of find and selecting
		}

		private void lbPrimitives_SelectedIndexChanged(object sender, EventArgs e)
		{
			mSelectedObject = lbPrimitives.SelectedValue;

			if (!initializing)
			{
				initializing = true;

				if (SelectMesh.OnlyObject != null && mSelectedObject != SelectMesh.OnlyObject)
				{
					SelectMesh.OnlyObject.Selected = mSelectedObject;
					SelectMesh.OnlyObject.Recreate();
				}


				if (ManagedMaterialInterfaceFound)
				{
					IManagedMaterial mmObj = (IManagedMaterial)mSelectedObject;
					Material mat = mmObj.Material;
					nudAmbientA.Value = (decimal)mat.Ambient.A;
					nudAmbientR.Value = (decimal)mat.Ambient.R;
					nudAmbientG.Value = (decimal)mat.Ambient.G;
					nudAmbientB.Value = (decimal)mat.Ambient.B;

					nudDiffuseA.Value = (decimal)mat.Diffuse.A;
					nudDiffuseR.Value = (decimal)mat.Diffuse.R;
					nudDiffuseG.Value = (decimal)mat.Diffuse.G;
					nudDiffuseB.Value = (decimal)mat.Diffuse.B;

					nudSpecularA.Value = (decimal)mat.Specular.A;
					nudSpecularR.Value = (decimal)mat.Specular.R;
					nudSpecularG.Value = (decimal)mat.Specular.G;
					nudSpecularB.Value = (decimal)mat.Specular.B;

					nudEmissiveA.Value = (decimal)mat.Emissive.A;
					nudEmissiveR.Value = (decimal)mat.Emissive.R;
					nudEmissiveG.Value = (decimal)mat.Emissive.G;
					nudEmissiveB.Value = (decimal)mat.Emissive.B;

					Color diffuseColor = Color.FromArgb((int)nudDiffuseA.Value, (int)nudDiffuseR.Value, (int)nudDiffuseG.Value, (int)nudDiffuseB.Value);
					Color ambientColor = Color.FromArgb((int)nudAmbientA.Value, (int)nudAmbientR.Value, (int)nudAmbientG.Value, (int)nudAmbientB.Value);
					Color specularColor = Color.FromArgb((int)nudSpecularA.Value, (int)nudSpecularR.Value, (int)nudSpecularG.Value, (int)nudSpecularB.Value);
					Color emissiveColor = Color.FromArgb((int)nudEmissiveA.Value, (int)nudEmissiveR.Value, (int)nudEmissiveG.Value, (int)nudEmissiveB.Value);

					lblAmbientColor.BackColor = ambientColor;
					lblDiffuseColor.BackColor = diffuseColor;
					lblSpecularColor.BackColor = specularColor;
					lblEmissiveColor.BackColor = emissiveColor;

					pnlMaterial.Enabled = true;
				}
				else
					pnlMaterial.Enabled = false;

				pgForms.SelectedObjects = (from object o in GetSelectedObjectsFromList() where o.GetType().BaseType == typeof(Form) select o as Form).ToArray<Form>();
				pgTypeViewer.SelectedObjects = GetSelectedObjectTypes();
				SetTypeViewer();
				SetMemberViewer();

                //SETUP THE Properties Grid for the selected item or items
				if (lbPrimitives.SelectedItems.Count == 0)
                    pgPrimitive.SelectedObject = null;
                else// if (lbPrimitives.SelectedItems.Count > 1)
                {
 					object[] primObjectList = GetSelectedObjectsFromList();
                    pgPrimitive.SelectedObjects = primObjectList;
                }
				pgModelView.SelectedObjects = GetSelectedModelViewObjectsFromList().Cast<object>().ToArray();

			}
			initializing = false;

		}

		private List<TransformMatrix> GetSelectedModelViewObjectsFromList()
		{
			List<TransformMatrix> modelViewObjectsFromList = new List<TransformMatrix>();
			 foreach (object obj in GetSelectedObjectsFromList())
			 {
				if (obj is IModelTransformable)
				{
					TransformMatrix mv = ((IModelTransformable)obj).ModelView;
					if (mv != null)
						modelViewObjectsFromList.Add(mv);
				}
			 }
			 return modelViewObjectsFromList;
		}
		private List<IRecreatable> GetSelectedRecreatablesFromList()
		{
			List<IRecreatable> recreatables = new List<IRecreatable>();
			foreach (object obj in GetSelectedObjectsFromList())
			{
				if (obj is IRecreatable)
					recreatables.Add((IRecreatable)obj);
			}
			return recreatables;
		}
		private object[] GetSelectedObjectsFromList()
		{
			IQueryable queryable = lbPrimitives.SelectedItems.Cast<ObjectListItem>().AsQueryable();
			object[] primObjectList =
				(from object li in queryable
				 select
					 li.GetType() == typeof(ObjectListItem) ?
					 ((ObjectListItem)li).ObjectAssociated : li).ToArray<object>();
			return primObjectList;
		}

		private static bool ITypeFilter(Type t, Object obj)
		{
			return obj.ToString() == t.ToString();
		}

		bool ManagedMaterialInterfaceFound 
		{
			get { return mSelectedObject is IManagedMaterial; }
		}
		bool ManagedModelViewFound 
		{
			get { return mSelectedObject is IModelTransformable; }
		}
		bool VertexBufferInterfaceFound 
		{
			get { return mSelectedObject is IManagedVertexBuffer; }
		}
		bool IndexBufferInterfaceFound 
		{
			get { return mSelectedObject is IManagedIndexBuffer; }
		}
		bool ColorableInterfaceFound
		{
			get { return mSelectedObject is IColorable; }
		}


		private void PrimitivesEditorForm_FormClosing(object sender, FormClosingEventArgs e)
		{
			if (e.CloseReason == CloseReason.UserClosing)
			{
				this.Hide();
				e.Cancel = true;
			}
		}


		//private void nudPosition_All_ValueChanged(object sender, EventArgs epsilon)
		//{
		//    if (!initializing && ManagedModelViewFound)
		//    {
		//        IModelTransformable mvObj = (IModelTransformable)mSelectedObject;
		//        Vector3 position = new Vector3((float)nudPositionX.Value, (float)nudPositionY.Value, (float)nudPositionZ.Value);
		//        mvObj.Transform.Position = position;
		//        mvObj.Recreate();
		//        if (tsmiSetCameraLookAtPosition.Checked)
		//        {
		//            mMainProgram.DefaultCameraForm.CameraTarget = position;
		//            mMainProgram.DefaultCameraForm.SetViewTransform();
		//        }
		//    }
		//}

		private void nudMaterial_All_ValueChanged(object sender, EventArgs e)
		{
			if (!initializing && ManagedMaterialInterfaceFound)
			{
				Color diffuseColor = Color.FromArgb((int)nudDiffuseA.Value, (int)nudDiffuseR.Value, (int)nudDiffuseG.Value, (int)nudDiffuseB.Value);
				Color ambientColor = Color.FromArgb((int)nudAmbientA.Value, (int)nudAmbientR.Value, (int)nudAmbientG.Value, (int)nudAmbientB.Value);
				Color specularColor = Color.FromArgb((int)nudSpecularA.Value, (int)nudSpecularR.Value, (int)nudSpecularG.Value, (int)nudSpecularB.Value);
				Color emissiveColor = Color.FromArgb((int)nudEmissiveA.Value, (int)nudEmissiveR.Value, (int)nudEmissiveG.Value, (int)nudEmissiveB.Value);

				lblEmissiveColor.BackColor = emissiveColor;
				lblSpecularColor.BackColor = specularColor;
				lblAmbientColor.BackColor = ambientColor;
				lblDiffuseColor.BackColor = diffuseColor;

				foreach(object o in lbPrimitives.SelectedItems)
				{
					if (o is IManagedMaterial)
					{
						IManagedMaterial mmObj = (IManagedMaterial)mSelectedObject;
						Material mat = new Material();


						mat.Ambient = ambientColor;
						mat.Diffuse = diffuseColor;
						mat.Specular = specularColor;
						mat.Emissive = emissiveColor;
					}
				}
			}
		}


        private void tsmiDisplayPrimitives_CheckedChanged(object sender, EventArgs e)
        {
            ReloadList();
        }

		private void cmdShowForm_Click(object sender, EventArgs e)
		{
			foreach (object o in pgForms.SelectedObjects)
			{
				Form f = null;
				try
				{
					f = (Form)o;
				}
				catch (Exception ex) { }
				
				if (f!=null)
				{
					f.Show();
					f.TopMost = true;
					f.WindowState = FormWindowState.Normal;
				}
			}
		}

		private void tsmiRecreatePrimitive_Click(object sender, EventArgs e)
		{
			foreach (object o in lbPrimitives.SelectedItems)
			{
				Object theObject = ((ObjectListItem)o).ObjectAssociated;

				if (theObject is IManagedVertexBuffer)
				{
					IManagedVertexBuffer vb = (IManagedVertexBuffer)theObject;
					vb.Recreate();
				}
			}
		}

		private void tsmiDeletePrimitive_Click(object sender, EventArgs e)
		{
			
			if (Selected != null)
			{
				DialogResult dr = System.Windows.Forms.MessageBox.Show("Would you really like to delete all the selected primitives?", "Confirmation", MessageBoxButtons.OKCancel, MessageBoxIcon.Warning);
				if (dr == DialogResult.OK)
				{

					List<ObjectListItem> removeList = lbPrimitives.SelectedItems.OfType<ObjectListItem>().ToList();

					foreach (ObjectListItem objToRmv in removeList)
					{
						object objAssoc = objToRmv.ObjectAssociated;
						object firstFind = null;

						mList.Remove(objToRmv);//mList is (List<ObjectListItem>)
						//mStaticObjectsList.Remove(objAssoc);

						try
						{
							if (mUserObjectList.Count > 0)
								firstFind = mUserObjectList.First<object>(delegate(object o) { return o == objAssoc; });
							if (firstFind != null)
							{
								mRenderer.UnregisterPrimitive(objAssoc);
								mUserObjectList.Remove(firstFind);
							}
						}
						catch (Exception ex)
						{
							//first throws an exception if not found
						}


						mSelectedObject = null;
						ReloadList();
					}
				}
			}
		}

		private void EnableVisibleSelectedPrimtives(bool enabled)
		{
			foreach (object o in lbPrimitives.SelectedItems)
			{
				Object theObject = ((ObjectListItem)o).ObjectAssociated;
				if (theObject is IManagedVertexBuffer)
				{
					IManagedVertexBuffer vb = (IManagedVertexBuffer)theObject;
					vb.EnableCallRenderPrimitives = enabled;
				}
				if (theObject is IManagedIndexBuffer)
				{
					IManagedIndexBuffer ib = (IManagedIndexBuffer)theObject;
					ib.EnableCallRenderIndexedPrimitives = enabled;
				}
			}
		}

		private void tsmiHideSelected_Click(object sender, EventArgs e)
		{
			EnableVisibleSelectedPrimtives(false);			
		}

		private void tsmiEnableVertexAndIndexRendering_Click(object sender, EventArgs e)
		{
			EnableVisibleSelectedPrimtives(true);
		}

		private void pgPrimitive_PropertyValueChanged(object s, PropertyValueChangedEventArgs e)
		{
			//if (epsilon.ChangedItem
			foreach (object o in pgPrimitive.SelectedObjects)
			{
				if (o is IRecreatable)
				{
					IRecreatable rec = (IRecreatable)o;
					rec.Recreate();
				}
			}
		}

		private void tsmiRestartAnimatedObject_Click(object sender, EventArgs e)
		{
			foreach (object sel in lbPrimitives.SelectedItems)
			{
				if (sel is IAnimated)
				{
					IAnimated an = (IAnimated)sel;
					an.ResetAnimated();
				}	
			}
		
		}

		private void lbPrimitives_DoubleClick(object sender, EventArgs e)
		{
			object sel = Selected;
			if (sel.GetType().BaseType == typeof(Form))//if forms are equal
			{
				Form f =(Form)sel;
				ShowForm(f);
			}
		}

		private static void ShowForm(Form f)
		{
			f.WindowState = FormWindowState.Normal;
			f.Show();// mSelectedPrimitive.GetType().Name;
			f.TopMost = true;
		}

		private void SetPGBrowsableAttributes(bool enabled)
		{
			if (enabled)
				this.pgPrimitive.BrowsableAttributes = new System.ComponentModel.AttributeCollection(new PGBrowsableAttribute());
			else
				this.pgPrimitive.BrowsableAttributes = null;
		}

		private object[] GetSelectedObjectTypes()
		{
			IQueryable theSelectedObjects = pgPrimitive.SelectedObjects.AsQueryable<object>();
			object[] selectedTypes = (from object selObj in theSelectedObjects select selObj.GetType()).ToArray<Type>();
			return selectedTypes;
		}


		private void SetTypeViewer()
		{
			List<Type> interfaces = null;
			foreach (Object o in (object[]) GetSelectedObjectsFromList())//pgTypeViewer.SelectedObjects)
			{
				Type t = o.GetType();
				if (interfaces == null)
					interfaces = t.GetInterfaces().ToList<Type>();
				else
					interfaces = t.GetInterfaces().Intersect<Type>(interfaces).ToList<Type>();
			}
			if (interfaces == null)
			{
				lbInterfaces.DataSource = null;
			}
			else
			{
				lbInterfaces.DataSource = interfaces.Cast<object>().ToArray<object>();
				lbInterfaces.DisplayMember = "FullName";
			}
		}
		private void SetMemberViewer()
		{
			List<MemberInfo> members = null;//System.Reflection

			foreach (Object o in (object[])GetSelectedObjectsFromList())
			{
				Type t = o.GetType();
				if (members == null)
					members = t.GetMembers().ToList<MemberInfo>();
				else
				{
					MemberInfoComparer<MemberInfo> eq = new MemberInfoComparer<MemberInfo>();
					members = t.GetMembers().Intersect<MemberInfo>(members, eq).ToList<MemberInfo>();
				}
			}
			
			lbMembers.DataSource = members;

		}


		private void tsmiRecreateSelected_Click(object sender, EventArgs e)
		{
			RecreateSelectedRecreatables();
		}

		private void RecreateSelectedRecreatables()
		{
			foreach (IRecreatable mvb in GetSelectedRecreatablesFromList())
			{
				mvb.Recreate();
			}
		}

		private void pgModelView_PropertyValueChanged(object s, PropertyValueChangedEventArgs e)
		{
			//RecreateSelectedRecreatables();
			IModelTransformable[] modelTransformables = (from object o in GetSelectedObjectsFromList() where o is IModelTransformable select o as IModelTransformable).ToArray<IModelTransformable>();

			foreach (IModelTransformable mt in modelTransformables)
			{
				if (mt is IManagedModelTransformable)
					((IManagedModelTransformable)mt).UpdateTransform();
				else if (mt is IRecreatable)
				{
					((IRecreatable)mt).Recreate();
				}
			}
		}

		private void btnSelectMesh_Click(object sender, EventArgs e)
		{
			SelectMesh.OnlyObject.Selected = ObjectsEditor.Selected;
			SelectMesh.OnlyObject.Recreate();
		}

		private void tsmiDisplayStaticObjects_CheckedChanged(object sender, EventArgs e)
		{
			ReloadList();
		}

		private void tsmiCustomAttributesOnly_Click(object sender, EventArgs e)
		{
			SetPGBrowsableAttributes(tsmiCustomAttributesOnly.Checked);
		}

		private void btnToggleMeshVisibility_Click(object sender, EventArgs e)
		{
			SelectMesh s = SelectMesh.OnlyObject;
			if (s != null)
				s.Visible = !s.Visible;
		}

		
		public class MemberInfoComparer<MemberInfo> : IEqualityComparer<MemberInfo>
		{
			public MemberInfoComparer()
			{

			}
			public bool Equals(MemberInfo x, MemberInfo y)
			{
				return ((MemberInfo)x).ToString() == ((MemberInfo)y).ToString();
			}
			public int GetHashCode(MemberInfo obj)
			{
				return obj.ToString().ToLower().GetHashCode();
			}

		}
	}


}
